package sdcncsi.ict.counter;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.dao.DataAccessException;

public class CounterCache {

	static final LinkedHashMap<String,VisitRecord> tempRecordMap = new LinkedHashMap<String,VisitRecord>();//访问记录临时Map
	static boolean tempRecordMutex = false;
	private static int tempRecordMutexNum = 0;
	static{
		Thread counterDBWriter = new Thread(new CounterDBThread());
		counterDBWriter.start();
		/*
		//添加写访问信息到数据库quartz job
		String triggerName  = "Counter_DB_Writer";
		boolean notAdded = true;
		try{
			String sql = "select count(*) from qrtz_triggers where trigger_name='"+triggerName+"'";
			int c = Db.getJtN().queryForInt(sql);
			if(c>0){
				notAdded = false;//'写访问信息到数据库 quartz job'已经添加
			}
			if(notAdded){
				QuartzApi quartzA = new QuartzApi();
				String cronExpr = "43 * * * * ?";//每分钟43秒时执行
				quartzA.addCronJob(cronExpr, CounterDBWriteJob.class, new HashMap(), true, triggerName);
			}
			LogItem logItem = new LogItem();// 日志项
			logItem.setClassName(CounterCache.class.getName());
			logItem.setMethod("clinit");
			logItem.setLevel("info");
			logItem.setDesc("已完成添加'写访问信息到数据库'任务:Counter_DB_Writer <CounterDBWriteJob>");
			Log.write(logItem);
		}catch(Exception e) {
			LogItem logItem = new LogItem();// 日志项
			logItem.setClassName(CounterCache.class.getName());
			logItem.setMethod("clinit");
			logItem.setLevel("error");
			logItem.setDesc("添加<CounterDBWriteJob>时出现异常");
			logItem.setContent(e.toString());
			Log.write(logItem);
		}
		*/
	}
	private static final LinkedHashMap counterNumberMap = new LinkedHashMap();//计数器统计缓存
	private static boolean counterNumberMutex = false;//获得计数器统计值类锁
	private static int counterNumberMutexNum = 0;
	private static final LRUMapCache<String,VisitItem> globalCache = new LRUMapCache<String,VisitItem>(CounterService.MAX_CACHES);//全局缓存
	static{
		try{//汇总访问项,从数据库获得已有的访问项
			String sql = "select * from ("+
				" select id,os as itemstr,counter_id,app_id,cjsj from t_counter_os where zt_dm=1"+
				" union"+
				" select id,browser as itemstr,counter_id,app_id,cjsj from t_counter_browser where zt_dm=1"+
				" union"+
				" select id,ip as itemstr,counter_id,app_id,cjsj from t_counter_ip where zt_dm=1"+
				" union"+
				" select id,uv as itemstr,counter_id,app_id,cjsj from t_counter_uv where zt_dm=1"+
				" union"+
				" select id,url as itemstr,counter_id,app_id,cjsj from t_counter_referer where zt_dm=1"+
				" )as visititem order by cjsj desc limit "+CounterService.MAX_CACHES;
			List visitItemList = DBTool.getJdbcTemplate().queryForList(sql);
			Iterator itr = visitItemList.iterator();
			while(itr.hasNext()){
				//获得计数器访问项
				Map visitItemMap = (Map)itr.next();
				String visitItemKey = visitItemMap.get("app_id").toString()+"_"+visitItemMap.get("counter_id")+"_"+visitItemMap.get("itemstr");
				int id = (Integer)visitItemMap.get("id");
				VisitItem visitItem = new VisitItem(id,false);
				globalCache.put(visitItemKey, visitItem);
			}
		}catch (DataAccessException e) {}
	}
	
	/**
	 * 添加一条访问记录到临时Map
	 * @param key
	 * @param visitRecord
	 */
	public static void addVisitRecord(String key,VisitRecord visitRecord){
		while(tempRecordMutex){//被锁住，等待，CounterDBThread解锁
			tempRecordMutexNum++;//做些不痛不痒的事情
		}
		counterNumberMutexNum = tempRecordMutexNum;//随便引用，防止java编译优化掉
		tempRecordMap.put(key, visitRecord);
	}
	
	/**
	 * 从全局缓存得到访问记录
	 */
	public static VisitItem getGlobalCache(int appID, String counterID, String visitItemStr){
		String key = appID+"_"+counterID+"_"+visitItemStr;
		return globalCache.get(key);
	}
	
	/**
	 * 添加记录到全局缓存
	 */
	public static VisitItem putGlobalCache(int appID, String counterID, String visitItemStr, VisitItem visitItem){
		String key = appID+"_"+counterID+"_"+visitItemStr;
		return globalCache.put(key, visitItem);
	}

	/**
	 * 得到appID应用->counterID计数器->当前计数值结构,没有则创建添加
	 */
	public static CounterNumber getOrAddCunnterNumber(int appID, String counterID){
		CounterNumber cn = null;
		LRUMapCache<String,CounterNumber> appMap = (LRUMapCache<String,CounterNumber>)counterNumberMap.get(appID);
		if(appMap==null){//如果没有为此应用添加计数缓存
			if(CounterService.getCounterApp(appID)!=null){//是否是合法的计数器应用
				appMap = new LRUMapCache<String,CounterNumber>(CounterService.APP_COUNTERS);//为该应用新建缓存计数器
				counterNumberMap.put(appID, appMap);
			}
		}
		if(appMap!=null){
			while(counterNumberMutex){//被锁住，等待，防止计数值被重写
				counterNumberMutexNum++;//做些不痛不痒的事情
			}
			tempRecordMutexNum = counterNumberMutexNum;//随便引用，防止java编译优化掉
			counterNumberMutex = true;//锁住
			cn = (CounterNumber)appMap.get(counterID);
			if(cn==null){
				cn = new CounterNumber();//为计数器ID新建计数值结构
				appMap.put(counterID, cn);
				try{//尝试从数据库获得计数统计值，并更新数值（与getCunnterNumber()方法不一样）
					Map cnMap = DBTool.getJdbcTemplate().queryForMap("select * from t_counter where zt_dm=1 and id='"+counterID+"' and app_id="+appID);
					cn.setNewCounter(false);
					cn.setPV((Integer)cnMap.get("pv"));
					cn.setIP((Integer)cnMap.get("ip"));
					cn.setUV((Integer)cnMap.get("uv"));
					cn.setSS((Integer)cnMap.get("ss"));
				}catch(DataAccessException e){}
			}
			counterNumberMutex = false;//解锁
		}
		return cn;
	}
	
	/**
	 * 从缓存得到appID应用->counterID计数器->当前计数值结构
	 */
	public static CounterNumber getCunnterNumber(int appID, String counterID){
		CounterNumber cn = null;
		LRUMapCache<String,CounterNumber> appMap = (LRUMapCache<String,CounterNumber>)counterNumberMap.get(appID);
		if(appMap==null){
			if(CounterService.getCounterApp(appID)!=null){
				appMap = new LRUMapCache<String,CounterNumber>(CounterService.APP_COUNTERS);//为该应用新建缓存计数器
				counterNumberMap.put(appID, appMap);
			}
		}
		if(appMap!=null){
			cn = (CounterNumber)appMap.get(counterID);
			if(cn==null){
				try{//尝试从数据库获得计数统计值，没有则不添加（与getOrAddCunnterNumber()方法不一样）
					Map cnMap = DBTool.getJdbcTemplate().queryForMap("select * from t_counter where zt_dm=1 and id='"+counterID+"' and app_id="+appID);
					cn = new CounterNumber();//为改计数器ID新建计数值结构
					appMap.put(counterID, cn);
					cn.setNewCounter(false);
					cn.setPV((Integer)cnMap.get("pv"));
					cn.setIP((Integer)cnMap.get("ip"));
					cn.setUV((Integer)cnMap.get("uv"));
					cn.setSS((Integer)cnMap.get("ss"));
				}catch(DataAccessException e){}
			}
		}
		return cn;
	}
}
